package com.gomcarter.frameworks.base.converter.enums;

import com.gomcarter.frameworks.base.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;

/**
 * 扩展 org.springframework.core.convert.support.StringToEnumConverterFactory
 * 扩展 org.springframework.core.convert.support.IntegerToEnumConverterFactory
 * Created by daibing on 2024/1/27.
 */
@Slf4j
@SuppressWarnings({"rawtypes", "unchecked"})
public class RequestParamToEnumConverterFactory implements ConverterFactory<String, Enum> {
    private final String enumResolverMethodName;

    public RequestParamToEnumConverterFactory(String enumResolverMethodName) {
        this.enumResolverMethodName = enumResolverMethodName;
    }

    @Override
    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        return new StringToEnum(EnumConverterManager.getEnumType(targetType), enumResolverMethodName);
    }

    private static class StringToEnum<T extends Enum> implements Converter<String, T> {
        private final Class<T> enumType;
        private final String enumResolverMethodName;

        public StringToEnum(Class<T> enumType, String enumResolverMethodName) {
            this.enumType = enumType;
            this.enumResolverMethodName = enumResolverMethodName;
        }

        @Override
        @Nullable
        public T convert(String source) {
            if (source.isEmpty()) {
                // It's an empty enum identifier: reset the enum value to null.
                return null;
            }
            try {
                // 通过Enum.valueOf转换
                return (T) Enum.valueOf(this.enumType, source.trim());
            } catch (NullPointerException | IllegalArgumentException e) {
                try {
                    // 通过Enum的自定义静态方法转换
                    Method resolverMethod = EnumConverterManager.getDeclaredMethod(enumType, enumResolverMethodName);
                    Object arg = EnumConverterManager.argIsNumber(enumType, enumResolverMethodName) ? Integer.parseInt(source.trim()) : source.trim();
                    return Optional
                            .ofNullable((T) resolverMethod.invoke(null, arg))
                            .orElseThrow(() -> new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + arg));
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e2) {
                    log.warn("通过Enum的自定义静态方法转换失败：enumType={}, enumResolverMethodName={}, error=", enumType.getName(), enumResolverMethodName, e2);
                    throw new CustomException(e2);
                }
            }
        }
    }

}
